const WorkspaceGroup = GObject.registerClass(
class WorkspaceGroup extends Clutter.Actor {
- _init(workspace, movingWindow) {
+ _init(workspace, monitor, movingWindow) {
super._init();
this._workspace = workspace;
+ this._monitor = monitor;
this._movingWindow = movingWindow;
this._windowRecords = [];
if (!window.showing_on_its_workspace())
return false;
+ const geometry = global.display.get_monitor_geometry(this._monitor.index);
+ const [intersects] = window.get_frame_rect().intersect(geometry);
+ if (!intersects)
+ return false;
+
const isSticky =
window.is_on_all_workspaces() || window === this._movingWindow;
for (const windowActor of windowActors) {
const clone = new Clutter.Clone({
source: windowActor,
- x: windowActor.x,
- y: windowActor.y,
+ x: windowActor.x - this._monitor.x,
+ y: windowActor.y - this._monitor.y,
});
this.add_child(clone);
}
});
-const MonitorGroup = GObject.registerClass(
-class MonitorGroup extends Clutter.Actor {
- _init(monitor) {
- super._init();
+const MonitorGroup = GObject.registerClass({
+ Properties: {
+ 'progress': GObject.ParamSpec.double(
+ 'progress', 'progress', 'progress',
+ GObject.ParamFlags.READWRITE,
+ -Infinity, Infinity, 0),
+ },
+}, class MonitorGroup extends Clutter.Actor {
+ _init(monitor, workspaceIndices, movingWindow) {
+ super._init({
+ clip_to_allocation: true,
+ });
+
+ this._monitor = monitor;
const constraint = new Layout.MonitorConstraint({ index: monitor.index });
this.add_constraint(constraint);
this.add_child(background);
+ this._container = new Clutter.Actor();
+ this.add_child(this._container);
+
+ const stickyGroup = new WorkspaceGroup(null, monitor, movingWindow);
+ this.add_child(stickyGroup);
+
+ this._workspaceGroups = [];
+
+ const workspaceManager = global.workspace_manager;
+ const vertical = workspaceManager.layout_rows === -1;
+ const activeWorkspace = workspaceManager.get_active_workspace();
+
+ let x = 0;
+ let y = 0;
+
+ for (const i of workspaceIndices) {
+ const ws = workspaceManager.get_workspace_by_index(i);
+ const fullscreen = ws.list_windows().some(w => w.get_monitor() === monitor.index && w.is_fullscreen());
+
+ if (i > 0 && vertical && !fullscreen && monitor.index === Main.layoutManager.primaryIndex) {
+ // We have to shift windows up or down by the height of the panel to prevent having a
+ // visible gap between the windows while switching workspaces. Since fullscreen windows
+ // hide the panel, they don't need to be shifted up or down.
+ y -= Main.panel.height;
+ }
+
+ const group = new WorkspaceGroup(ws, monitor, movingWindow);
+
+ this._workspaceGroups.push(group);
+ this._container.add_child(group);
+ group.set_position(x, y);
+
+ if (vertical)
+ y += this.baseDistance;
+ else if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL)
+ x -= this.baseDistance;
+ else
+ x += this.baseDistance;
+ }
+
+ this.progress = this.getWorkspaceProgress(activeWorkspace);
+
this._bgManager = new Background.BackgroundManager({
container: background,
monitorIndex: monitor.index,
_onDestroy() {
this._bgManager.destroy();
}
+
+ get baseDistance() {
+ if (global.workspace_manager.layout_rows === -1)
+ return this._monitor.height;
+ else
+ return this._monitor.width;
+ }
+
+ get progress() {
+ if (global.workspace_manager.layout_rows === -1)
+ return -this._container.y / this.baseDistance;
+ else if (this.get_text_direction() === Clutter.TextDirection.RTL)
+ return this._container.x / this.baseDistance;
+ else
+ return -this._container.x / this.baseDistance;
+ }
+
+ set progress(p) {
+ if (global.workspace_manager.layout_rows === -1)
+ this._container.y = -Math.round(p * this.baseDistance);
+ else if (this.get_text_direction() === Clutter.TextDirection.RTL)
+ this._container.x = Math.round(p * this.baseDistance);
+ else
+ this._container.x = -Math.round(p * this.baseDistance);
+ }
+
+ get index() {
+ return this._monitor.index;
+ }
+
+ getWorkspaceProgress(workspace) {
+ const group = this._workspaceGroups.find(g =>
+ g.workspace.index() === workspace.index());
+ return this._getWorkspaceGroupProgress(group);
+ }
+
+ _getWorkspaceGroupProgress(group) {
+ if (global.workspace_manager.layout_rows === -1)
+ return group.y / this.baseDistance;
+ else if (this.get_text_direction() === Clutter.TextDirection.RTL)
+ return -group.x / this.baseDistance;
+ else
+ return group.x / this.baseDistance;
+ }
+
+ getSnapPoints() {
+ return this._workspaceGroups.map(g =>
+ this._getWorkspaceGroupProgress(g));
+ }
+
+ findClosestWorkspace(progress) {
+ const distances = this.getSnapPoints().map(p =>
+ Math.abs(p - progress));
+ const index = distances.indexOf(Math.min(...distances));
+ return this._workspaceGroups[index].workspace;
+ }
+
+ _interpolateProgress(progress, monitorGroup) {
+ if (this.index === monitorGroup.index)
+ return progress;
+
+ const points1 = monitorGroup.getSnapPoints();
+ const points2 = this.getSnapPoints();
+
+ const upper = points1.indexOf(points1.find(p => p >= progress));
+ const lower = points1.indexOf(points1.slice().reverse().find(p => p <= progress));
+
+ if (points1[upper] === points1[lower])
+ return points2[upper];
+
+ const t = (progress - points1[lower]) / (points1[upper] - points1[lower]);
+
+ return points2[lower] + (points2[upper] - points2[lower]) * t;
+ }
+
+ updateSwipeForMonitor(progress, monitorGroup) {
+ this.progress = this._interpolateProgress(progress, monitorGroup);
+ }
});
var WorkspaceAnimationController = class {
return;
const workspaceManager = global.workspace_manager;
- const vertical = workspaceManager.layout_rows === -1;
const nWorkspaces = workspaceManager.get_n_workspaces();
- const activeWorkspaceIndex = workspaceManager.get_active_workspace_index();
const switchData = {};
this._switchData = switchData;
- switchData.stickyGroup = new WorkspaceGroup(null, this.movingWindow);
- switchData.workspaceGroups = [];
+ switchData.monitors = [];
+
switchData.gestureActivated = false;
switchData.inProgress = false;
- switchData.container = new Clutter.Actor();
- switchData.backgroundGroup = new Clutter.Actor();
-
- for (const monitor of Main.layoutManager.monitors)
- switchData.backgroundGroup.add_child(new MonitorGroup(monitor));
-
- Main.uiGroup.insert_child_above(switchData.backgroundGroup, global.window_group);
- Main.uiGroup.insert_child_above(switchData.container, switchData.backgroundGroup);
- Main.uiGroup.insert_child_above(switchData.stickyGroup, switchData.container);
-
- let x = 0;
- let y = 0;
-
if (!workspaceIndices)
workspaceIndices = [...Array(nWorkspaces).keys()];
- for (const i of workspaceIndices) {
- const ws = workspaceManager.get_workspace_by_index(i);
- const fullscreen = ws.list_windows().some(w => w.is_fullscreen());
+ const monitors = Meta.prefs_get_workspaces_only_on_primary()
+ ? [Main.layoutManager.primaryMonitor] : Main.layoutManager.monitors;
- if (y > 0 && vertical && !fullscreen) {
- // We have to shift windows up or down by the height of the panel to prevent having a
- // visible gap between the windows while switching workspaces. Since fullscreen windows
- // hide the panel, they don't need to be shifted up or down.
- y -= Main.panel.height;
- }
+ for (const monitor of monitors) {
+ if (Meta.prefs_get_workspaces_only_on_primary() &&
+ monitor.index !== Main.layoutManager.primaryIndex)
+ continue;
- const group = new WorkspaceGroup(ws, this.movingWindow);
+ const group = new MonitorGroup(monitor, workspaceIndices, this.movingWindow);
- switchData.workspaceGroups.push(group);
- switchData.container.add_child(group);
- switchData.container.set_child_above_sibling(group, null);
- group.set_position(x, y);
+ Main.uiGroup.insert_child_above(group, global.window_group);
- if (vertical)
- y += global.screen_height;
- else if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL)
- x -= global.screen_width;
- else
- x += global.screen_width;
+ switchData.monitors.push(group);
}
-
- const activeGroup = this._findWorkspaceGroupByIndex(activeWorkspaceIndex);
- if (vertical)
- switchData.container.y = -activeGroup.y;
- else
- switchData.container.x = -activeGroup.x;
}
_finishWorkspaceSwitch(switchData) {
this._switchData = null;
- switchData.backgroundGroup.destroy();
- switchData.container.destroy();
- switchData.stickyGroup.destroy();
+ switchData.monitors.forEach(m => m.destroy());
this.movingWindow = null;
}
this._prepareWorkspaceSwitch(workspaceIndices);
this._switchData.inProgress = true;
- const fromGroup = this._findWorkspaceGroupByIndex(from);
- const toGroup = this._findWorkspaceGroupByIndex(to);
-
- this._switchData.container.x = -fromGroup.x;
- this._switchData.container.y = -fromGroup.y;
-
- this._switchData.container.ease({
- x: -toGroup.x,
- y: -toGroup.y,
- duration: WINDOW_ANIMATION_TIME,
- mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
- onComplete: () => {
- this._finishWorkspaceSwitch(this._switchData);
- onComplete();
- this._swipeTracker.enabled = true;
- },
- });
- }
+ const fromWs = global.workspace_manager.get_workspace_by_index(from);
+ const toWs = global.workspace_manager.get_workspace_by_index(to);
- _getProgressForWorkspace(workspaceGroup) {
- if (global.workspace_manager.layout_rows === -1)
- return workspaceGroup.y / global.screen_height;
- else if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL)
- return -workspaceGroup.x / global.screen_width;
- else
- return workspaceGroup.x / global.screen_width;
- }
+ for (const monitorGroup of this._switchData.monitors) {
+ monitorGroup.progress = monitorGroup.getWorkspaceProgress(fromWs);
+ const progress = monitorGroup.getWorkspaceProgress(toWs);
+
+ const params = {
+ duration: WINDOW_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
+ };
+
+ if (monitorGroup.index === Main.layoutManager.primaryIndex) {
+ params.onComplete = () => {
+ this._finishWorkspaceSwitch(this._switchData);
+ onComplete();
+ this._swipeTracker.enabled = true;
+ };
+ }
- _findWorkspaceGroupByIndex(index) {
- return this._switchData.workspaceGroups.find(g => g.workspace.index() === index);
+ monitorGroup.ease_property('progress', progress, params);
+ }
}
- _findClosestWorkspaceGroup(progress) {
- const distances = this._switchData.workspaceGroups.map(g => {
- const workspaceProgress = this._getProgressForWorkspace(g);
- return Math.abs(workspaceProgress - progress);
- });
- const index = distances.indexOf(Math.min(...distances));
- return this._switchData.workspaceGroups[index];
+ _findMonitorGroup(monitorIndex) {
+ return this._switchData.monitors.find(m => m.index === monitorIndex);
}
_switchWorkspaceBegin(tracker, monitor) {
? Clutter.Orientation.HORIZONTAL
: Clutter.Orientation.VERTICAL;
- const baseDistance = horiz ? global.screen_width : global.screen_height;
-
- let progress;
- let cancelProgress;
if (this._switchData && this._switchData.gestureActivated) {
- this._switchData.container.remove_all_transitions();
- if (!horiz)
- progress = -this._switchData.container.y / baseDistance;
- else if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL)
- progress = this._switchData.container.x / baseDistance;
- else
- progress = -this._switchData.container.x / baseDistance;
-
- const wsGroup = this._findClosestWorkspaceGroup(progress);
- cancelProgress = this._getProgressForWorkspaceGroup(wsGroup);
+ for (const group of this._switchData.monitors)
+ group.remove_all_transitions();
} else {
this._prepareWorkspaceSwitch();
+ }
- const activeIndex = workspaceManager.get_active_workspace_index();
- const wsGroup = this._findWorkspaceGroupByIndex(activeIndex);
+ const monitorGroup = this._findMonitorGroup(monitor);
+ const baseDistance = monitorGroup.baseDistance;
+ const progress = monitorGroup.progress;
- progress = cancelProgress = this._getProgressForWorkspace(wsGroup);
- }
+ const closestWs = monitorGroup.findClosestWorkspace(progress);
+ const cancelProgress = monitorGroup.getWorkspaceProgress(closestWs);
+ const points = monitorGroup.getSnapPoints();
- const points = this._switchData.workspaceGroups.map(this._getProgressForWorkspace);
+ this._switchData.baseMonitorGroup = monitorGroup;
tracker.confirmSwipe(baseDistance, points, progress, cancelProgress);
}
if (!this._switchData)
return;
- let xPos = 0;
- let yPos = 0;
-
- if (global.workspace_manager.layout_rows === -1)
- yPos = -Math.round(progress * global.screen_height);
- else if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL)
- xPos = Math.round(progress * global.screen_width);
- else
- xPos = -Math.round(progress * global.screen_width);
-
- this._switchData.container.set_position(xPos, yPos);
+ for (const monitorGroup of this._switchData.monitors)
+ monitorGroup.updateSwipeForMonitor(progress, this._switchData.baseMonitorGroup);
}
_switchWorkspaceEnd(tracker, duration, endProgress) {
if (!this._switchData)
return;
- const newGroup = this._findClosestWorkspaceGroup(endProgress);
-
const switchData = this._switchData;
switchData.gestureActivated = true;
- this._switchData.container.ease({
- x: -newGroup.x,
- y: -newGroup.y,
- duration,
- mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
- onComplete: () => {
- if (!newGroup.workspace.active)
- newGroup.workspace.activate(global.get_current_time());
- this._finishWorkspaceSwitch(switchData);
- },
- });
+ const newWs = switchData.baseMonitorGroup.findClosestWorkspace(endProgress);
+
+ for (const monitorGroup of this._switchData.monitors) {
+ const progress = monitorGroup.getWorkspaceProgress(newWs);
+
+ const params = {
+ duration,
+ mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
+ };
+
+ if (monitorGroup.index === Main.layoutManager.primaryIndex) {
+ params.onComplete = () => {
+ if (!newWs.active)
+ newWs.activate(global.get_current_time());
+ this._finishWorkspaceSwitch(switchData);
+ };
+ }
+
+ monitorGroup.ease_property('progress', progress, params);
+ }
}
get gestureActive() {